package timingwheel

import (
	"fmt"
	"sync"
	"time"
)

// MultiLevelTimingWheel 定义多级时间轮的数据结构
type MultiLevelTimingWheel struct {
	// wheels 各级时间轮
	wheels []*SingleTimingWheel
	// taskChan 任务队列
	taskChan chan *Task
	// stopChan 停止信号
	stopChan chan struct{}

	waitGroup sync.WaitGroup
}

// NewMultilevelTimingWheel 新建一个的多级时间轮
func NewMultilevelTimingWheel(tickDuration time.Duration, slotNums []int) *MultiLevelTimingWheel {
	if len(slotNums) == 0 {
		panic("timingwheel: at least one time wheel level is required")
	}

	tws := &MultiLevelTimingWheel{
		wheels:   make([]*SingleTimingWheel, len(slotNums)),
		taskChan: make(chan *Task, 1024),
		stopChan: make(chan struct{}),
	}

	// 创建各级时间轮
	for i, slotNum := range slotNums {
		var tick time.Duration
		if i == 0 {
			tws.wheels[i] = NewSingleTimingWheel(tickDuration, slotNum)
			continue
		}

		// 上一级时间轮转动一圈的时间作为当前级的滴答时间
		tick = time.Duration(slotNums[i-1]) * tickDuration
		tws.wheels[i] = NewSingleTimingWheel(tick, slotNum)
	}

	return tws
}

// Start 启动多级时间轮
func (own *MultiLevelTimingWheel) Start() {
	// 启动各级时间轮
	for _, wheel := range own.wheels {
		wheel.Start()
	}

	own.waitGroup.Add(1)
	go own.run()
}

// AddTask 添加一个定时任务
func (own *MultiLevelTimingWheel) AddTask(delay time.Time, callback func(), repeating bool, repeats int, interval time.Duration) {
	// 若 repeats=0，相当于 repeating=false，即不重复执行
	if repeats == 0 {
		repeating = false
		interval = 0
	}

	// 添加任务到任务队列
	own.taskChan <- &Task{
		delay:       delay.UnixNano(),
		callback:    callback,
		interval:    interval,
		repeatTimes: repeats,
		repeating:   repeating,
	}
}

// Stop 停止多级时间轮
func (own *MultiLevelTimingWheel) Stop() {
	close(own.stopChan)
	own.waitGroup.Wait()

	// 停止各级时间轮
	for i := len(own.wheels) - 1; i >= 0; i-- {
		own.wheels[i].Stop()
	}
}

// run 运行时间轮
func (own *MultiLevelTimingWheel) run() {
	defer own.waitGroup.Done()

	for {
		select {
		case task := <-own.taskChan:
			own.dispatchTask(task)
		case <-own.stopChan:
			return
		}
	}
}

// dispatchTask 将任务分发到合适的时间轮
func (own *MultiLevelTimingWheel) dispatchTask(task *Task) {
	now := time.Now().UnixNano()
	exp := task.delay - now

	// 任务已经过期，立即执行
	if exp <= 0 {
		own.callback(task)
	}

	fmt.Println(task.repeating, task.repeatTimes, task.callbackTimes)

	// 检查是否是否还需要执行
	if !task.repeating || task.repeatTimes == task.callbackTimes {
		return
	}

	// 计算应该放入哪一级时间轮
	level := 0
	base := int64(own.wheels[0].tickDuration)
	for level < len(own.wheels)-1 {
		// 计算当前级时间轮的最大表示范围
		if exp < int64(own.wheels[level].slotNumber)*base {
			break
		}

		level++
		base *= int64(own.wheels[level-1].slotNumber)
	}

	fmt.Println(level, base)

	// 将任务添加到选中的时间轮
	wheel := own.wheels[level]
	wheel.AddTask(time.Now().Add(task.interval), func() {
		// 如果不是第一级时间轮，任务到期后重新放回任务队列，由更细粒度的时间轮处理
		if level > 0 {
			own.taskChan <- task
			return
		}

		// 第一级时间轮直接执行任务
		own.callback(task)

		// 如果是重复任务，重新计算到期时间并放回队列
		if task.repeating && task.repeatTimes != task.callbackTimes {
			task.delay = time.Now().UnixNano() + int64(task.interval)
			own.taskChan <- task
		}
	}, false, 0, 0)
}

func (own *MultiLevelTimingWheel) chooseWheelLevel(task *Task) int {
	exp := task.delay
	base := int64(own.wheels[0].tickDuration)
	for level := 0; level < len(own.wheels); level++ {
		maxExp := int64(own.wheels[level].slotNumber) * base
		if exp < maxExp {
			return level
		}

		// 准备下一级时间轮的基数
		if level < len(own.wheels)-1 {
			base *= int64(own.wheels[level].slotNumber)
		}
	}

	// 如果所有级别都无法容纳，放入最高级
	return len(own.wheels) - 1
}

func (own *MultiLevelTimingWheel) callback(task *Task) {
	go func() {
		own.waitGroup.Add(1)
		task.callback()
		task.callbackTimes++
		own.waitGroup.Done()
	}()
}
